﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;

#endif
#if USING_XLUA
using XLua;

#endif

namespace com.susi.util.toolkit {
    /// <summary>
    ///     提供高效简单的事件处理机制
    /// </summary>
#if ODIN_INSPECTOR
    [ShowInInspector] [LabelText("事件源")]
#endif
    public class EventSource : IEventSource, IListener {
        public static IEventSource Empty => EmptyEventSource.Instance;

        public bool IsDisposed { get; private set; }

#if ODIN_INSPECTOR
        [ShowInInspector] [LabelText("监听器")]
#endif
        private Dictionary<Type, LinkedList<Delegate>> CSDic = new Dictionary<Type, LinkedList<Delegate>>();
#if ODIN_INSPECTOR
        [ShowInInspector] [LabelText("监听器")]
#endif
        private LinkedList<IListener> CSLit;

#if USING_XLUA
#if ODIN_INSPECTOR
        [ShowInInspector, LabelText("Lua监听器")]
#endif
        private Dictionary<string, LinkedList<LuaTable>> LuaDic = new Dictionary<string, LinkedList<LuaTable>>();
#if ODIN_INSPECTOR
        [ShowInInspector, LabelText("监听器")]
#endif
        private LinkedList<LuaTable> LuaLit;
#endif
        public event Action               onDisposingEvent;
        public event Action<IEventSource> onDisposingTEvent;

        /// <summary>
        ///     指示这个事件源可以接受的消息类型
        /// </summary>
        public virtual IEnumerable<Type> ReceivableMessageType {
            get { return Assembly.GetExecutingAssembly().GetTypes().Where(v => v.IsStructAndExtend(typeof(IMessage))); }
        }

        /// <summary>
        ///     监听消息
        /// </summary>
        /// <typeparam name="T">消息类型</typeparam>
        /// <param name="onMsg">监听器</param>
        public void Add<T>(Action<T> onMsg) where T :  IMessage
        {
            CSDic.AddToInnerList(typeof(T), (Delegate) onMsg);
        }

        /// <summary>
        ///     在不能使用泛型的场合可用
        /// </summary>
        public void Add(IListener listener)
        {
            if (CSLit == null) CSLit = new LinkedList<IListener>();
            CSLit.AddLast(listener);
        }
#if USING_XLUA
        public void Add(string msgType, LuaTable table)
        {
            LuaDic.AddToInnerList(msgType, table);
        }

        public void Add(LuaTable table)
        {
            if (LuaLit == null) LuaLit = new LinkedList<LuaTable>();
            LuaLit.AddLast(table);
        }
#endif
        /// <summary>
        ///     取消监听消息
        /// </summary>
        /// <typeparam name="T">消息类型</typeparam>
        /// <param name="onMsg">监听器</param>
        public void Remove<T>(Action<T> onMsg) where T :  IMessage
        {
            CSDic.RemoveFromInnerList(typeof(T), (Delegate) onMsg);
        }

        /// <summary>
        ///     在不能使用泛型的场合可用
        /// </summary>
        public void Remove(IListener listener)
        {
            if (CSLit == null) return;
            CSLit.Remove(listener);
        }

#if USING_XLUA
        public void Remove(string msgType, LuaTable table)
        {
            LuaDic.RemoveFromInnerList(msgType, table);
        }

        public void Remove(LuaTable table)
        {
            LuaLit.Remove(table);
        }
#endif

        /// <summary>
        ///     发送消息,通常只限EventSource的持有者调用
        /// </summary>
        /// <typeparam name="T">消息类型</typeparam>
        /// <param name="msg">消息实体</param>
#if ODIN_INSPECTOR
        [Button("发送消息")]
#endif
        public void Send<T>(T msg) where T :  IMessage
        {
            if (CSDic.TryGetValue(typeof(T), out var lst))
                foreach (var del in lst)
                    ((Action<T>) del)(msg);
            if (CSLit != null)
                foreach (var lit in CSLit)
                    lit.OnMessage(msg);
        }

        /// <summary>
        /// 反射以正确调用对应泛型
        /// 如果可能，用<see cref="Send{T}"/>替代
        /// </summary>
        public void Send(IMessage msg)
        {
            var msgtype = msg.GetType();
            if (CSDic.TryGetValue(msgtype, out var lst))
                foreach (var del in lst) {
                    del.DynamicInvoke(msg);
                }

            if (CSLit != null)
                foreach (var lit in CSLit) {
                    var methodinf = typeof(IListener).GetMethod(nameof(IListener.OnMessage));
                    var genericMethod= methodinf.MakeGenericMethod(msgtype);
                    genericMethod.Invoke(lit, new object[]{msg});
                }
        }

        /// <summary>
        ///     当这个EventSource收到消息时处理后传递到下个Source
        ///     <para>会自动处理Disposable</para>
        /// </summary>
        public void OutLink(EventSource source)
        {
            if (source == this) throw new Exception("Can't be self");
            if (source == null) throw new ArgumentNullException();
            Add(source);
            source.onDisposingEvent += () => Remove(source);
        }
#if ODIN_INSPECTOR
        [Button("释放")]
#endif
        public void Dispose()
        {
            if (IsDisposed) return;
            IsDisposed = true;
            onDisposingEvent?.Invoke();
            onDisposingTEvent?.Invoke(this);
            onDisposingEvent  = null;
            onDisposingTEvent = null;
            CSDic.Clear();
            CSLit = null;
#if USING_XLUA
            LuaDic.Clear();
            LuaLit = null;
#endif
        }

        /// <summary>
        ///     等同Send
        /// </summary>
        void IListener.OnMessage<T>(T msg)
        {
            Send(msg);
        }
    }
}